﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using QuotingTool.Domain.Models;
using QuotingTool.SForce;
using System.Configuration;
using System.Net;
using System.Web.Services.Protocols;

namespace QuotingTool.Domain.DataAccess.Repositories
{
    public class SFRepository
    {
        private string connectionString;
        private SforceService binding;
        private LoginResult lr;

        public SFRepository(string connectionString)
        {
            this.connectionString = connectionString;

            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
        }


        /// <summary>
        /// Return account details by id
        /// </summary>
        /// <param name="AccountID"></param>
        /// <returns></returns>
        public SFAccount getAccountByID(string AccountID)
        {
            SFAccount AccountById = new SFAccount();
            bool bIsSFLogin = SFLogin();

            if (bIsSFLogin)
            {
                AccountById = QueryAccountByID(AccountID);
            }

            return AccountById;
        }


        private bool SFLogin()
        {
            string username = string.Empty;
            string password = string.Empty;
            bool bIsLogin = true;

            username = ConfigurationManager.AppSettings["username"];
            password = ConfigurationManager.AppSettings["password"];

            // Create a service object
            binding = new SforceService();
            // Timeout after a minute
            binding.Timeout = System.Convert.ToInt32(ConfigurationManager.AppSettings["SFTimeOutMs"]);
            // Try logging in


            lr = binding.login(username, password);

            if (lr != null)
            {
                // Check if the password has expired
                if (lr.passwordExpired)
                {
                    //       responseLogIn.isError = true;
                    //       responseLogIn.errorMessage = "An error has occurred. Your password has expired.";
                    //       return responseLogIn;
                }
                /** Once the client application has logged in successfully, it will use
                * the results of the login call to reset the endpoint of the service
                * to the virtual server instance that is servicing your organization
                */
                binding.Url = lr.serverUrl;
                /** The sample client application now has an instance of the SforceService
                * that is pointing to the correct endpoint. Next, the sample client
                * application sets a persistent SOAP header (to be included on all
                * subsequent calls that are made with SforceService) that contains the
                * valid sessionId for our login credentials. To do this, the sample
                * client application creates a new SessionHeader object and persist it to
                * the SforceService. Add the session ID returned from the login to the
                * session header
                */
                binding.SessionHeaderValue = new SessionHeader();
                binding.SessionHeaderValue.sessionId = lr.sessionId;
                // Return true to indicate that we are logged in, pointed
                // at the right URL and have our security token in place.
            }
            else
            {
                bIsLogin = false;
            }


            return bIsLogin;
        }


        private SFAccount QueryAccountByID(string AccountID)
        {
            //The results will be placed in qr
            QueryResult qr = null;
            SFAccount AccountByID = new SFAccount();

            //We are going to increase our return batch size to 250 items
            //Setting is a recommendation only, different batch sizes may
            //be returned depending on data, to keep performance optimized.
            binding.QueryOptionsValue = new QueryOptions();
            binding.QueryOptionsValue.batchSize = 1; //max seems to be 2000;
            binding.QueryOptionsValue.batchSizeSpecified = true;
            string sQuery = "select id,name,parent.name,parent.id from Account where id = '" + AccountID + "'";
            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                foreach (var item in qr.records)
                {
                    SForce.Account oAccount = item as SForce.Account;
                    SFAccount _acc = new SFAccount();

                    _acc.AccountID = oAccount.Id;
                    _acc.AccountName = oAccount.Name;
                    if (oAccount.Parent != null)
                    {
                        _acc.MasterAccountName = oAccount.Parent.Name;
                        _acc.MasterAccountID = oAccount.Parent.Id;
                    }
                    AccountByID = _acc;
                }
            }
            return AccountByID;
        }
        private List<SFAccount> QueryAccounts(string SearchName)
        {
            //The results will be placed in qr
            QueryResult qr = null;
            List<SFAccount> lstAccounts = new List<SFAccount>();

            //We are going to increase our return batch size to 250 items
            //Setting is a recommendation only, different batch sizes may
            //be returned depending on data, to keep performance optimized.
            binding.QueryOptionsValue = new QueryOptions();
            binding.QueryOptionsValue.batchSize = int.Parse(ConfigurationManager.AppSettings["SF_Accounts_Batch_Size"]); //max seems to be 2000;
            binding.QueryOptionsValue.batchSizeSpecified = true;
            string sQuery = "select id,name,parent.name,parent.id from Account where name like '" + SearchName + "%'";
            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                foreach (var item in qr.records)
                {
                    SForce.Account oAccount = item as SForce.Account;
                    SFAccount _acc = new SFAccount();

                    _acc.AccountID = oAccount.Id;
                    _acc.AccountName = oAccount.Name;
                    if (oAccount.Parent != null)
                    {
                        _acc.MasterAccountName = oAccount.Parent.Name;
                        _acc.MasterAccountID = oAccount.Parent.Id;
                    }
                    lstAccounts.Add(_acc);
                }
            }

            return lstAccounts;
        }



        public List<SFOpportunity> SearchOpp(string OppNumber, string OppName, string TopDivision, string OppOwner, string SFAccount)
        {
            binding = new SforceService();
            // Timeout after a minute
            binding.Timeout = System.Convert.ToInt32(ConfigurationManager.AppSettings["SFTimeOutMs"]);
            binding.SessionHeaderValue = new SessionHeader();
            binding.SessionHeaderValue.sessionId = string.Empty;

            List<SFOpportunity> lstOpps = new List<SFOpportunity>();

            //The results will be placed in qr
            QueryResult qr = null;
            List<SFOpportunity> lstOpportunities = new List<SFOpportunity>();



            string sWhereClause = "RecordTypeId='01250000000Qn6c'";

            if (!string.IsNullOrEmpty(OppNumber))
            {
                sWhereClause += " AND OP__c='" + OppNumber + "' ";
            }

            if (!string.IsNullOrEmpty(OppName))
            {
                if (!string.IsNullOrEmpty(sWhereClause))
                {
                    sWhereClause += "AND ";
                }

                sWhereClause += "Name='" + OppName + "' ";
            }


            if (!string.IsNullOrEmpty(TopDivision))
            {
                if (!string.IsNullOrEmpty(sWhereClause))
                {
                    sWhereClause += "AND ";
                }

                sWhereClause += "Top_Division__c='" + TopDivision + "' ";
            }


            if (!string.IsNullOrEmpty(OppOwner))
            {
                if (!string.IsNullOrEmpty(sWhereClause))
                {
                    sWhereClause += "AND ";
                }

                sWhereClause += "Owner.Name='" + OppOwner + "' ";
            }

            if (!string.IsNullOrEmpty(SFAccount))
            {
                if (!string.IsNullOrEmpty(sWhereClause))
                {
                    sWhereClause += "AND ";
                }

                sWhereClause += "account.id='" + SFAccount + "' ";
            }



            string sQuery = "select Nice_Forecast__c,StageName,account.parent.name,Owner.Name,account.name,account.id,account.parent.id, OP__c,Id,Name,Top_Division__c,Description,Amount,CloseDate,AccountId,Region__c from opportunity where " + sWhereClause;
            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                // SForce.Opportunity oOpp = qr.records[0] as SForce.Opportunity;
                foreach (SForce.Opportunity oOpp in qr.records)
                {
                    SFOpportunity _opp = new SFOpportunity();

                    if (oOpp.Account != null)
                    {
                        _opp.AccountName = oOpp.Account.Name;
                        _opp.AccountID = oOpp.Account.Id;

                        if (oOpp.Account.Parent != null)
                        {
                            _opp.MasterAccountName = oOpp.Account.Parent.Name;
                            _opp.MasterAccountID = oOpp.Account.Parent.Id;
                        }
                    }

                    _opp.OppNumber = oOpp.OP__c;
                    _opp.OppName = oOpp.Name;
                    _opp.OppID = oOpp.Id;
                    _opp.TopDivision = oOpp.Top_Division__c;
                    _opp.Owner = oOpp.Owner.Name;

                    if (oOpp.Amount != null)
                    {
                        _opp.Amount = oOpp.Amount.Value.ToString();
                    }
                    _opp.Stage = oOpp.StageName;
                    _opp.Forecast = oOpp.Nice_Forecast__c;

                    if (oOpp.CloseDate != null)
                    {
                        _opp.CloseDate = oOpp.CloseDate.ToString();
                    }
                    lstOpps.Add(_opp);
                }
            }

            return lstOpps;
        }




        public SFOpportunity GetOpportunity(string OppID)
        {
            SFOpportunity _opp = new SFOpportunity();
            // Create a service object
            binding = new SforceService();
            // Timeout after a minute
            binding.Timeout = System.Convert.ToInt32(ConfigurationManager.AppSettings["SFTimeOutMs"]);
            binding.SessionHeaderValue = new SessionHeader();
            binding.SessionHeaderValue.sessionId = string.Empty;

            //The results will be placed in qr
            QueryResult qr = null;
            List<SFOpportunity> lstOpportunities = new List<SFOpportunity>();

            //We are going to increase our return batch size to 250 items
            //Setting is a recommendation only, different batch sizes may
            //be returned depending on data, to keep performance optimized.
            binding.QueryOptionsValue = new QueryOptions();
            binding.QueryOptionsValue.batchSize = int.Parse(ConfigurationManager.AppSettings["SF_Accounts_Batch_Size"]); //max seems to be 2000;
            binding.QueryOptionsValue.batchSizeSpecified = true;
            /*
                        Customer type (Direct/Indirect) - 
            Maintenance policy (standard / enhanced..) - Maintenance_program__c
            Product (NIM / RTI ..) - Product_Family__c
            Maintenance %  -  Maintenance__c
            Maintenance source (List /Net) -  Main_Base_for_calculation__c
            Annual increase % (= CPI) - Annual_Increase__c
                        */


            string sQuery = "select account.parent.name,account.name,account.id,account.parent.id, OP__c,Id,Name,Top_Division__c,Description,Amount,CloseDate,AccountId,Region__c from opportunity where Id = '" + OppID + "'";
            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                SForce.Opportunity oOpp = qr.records[0] as SForce.Opportunity;

                if (oOpp.Account != null)
                {
                    _opp.AccountName = oOpp.Account.Name;
                    _opp.AccountID = oOpp.Account.Id;

                    if (oOpp.Account.Parent != null)
                    {
                        _opp.MasterAccountName = oOpp.Account.Parent.Name;
                        _opp.MasterAccountID = oOpp.Account.Parent.Id;
                    }
                }

                _opp.TopDivision = oOpp.Top_Division__c;
                _opp.OppID = oOpp.Id;
                _opp.OppAccountID = oOpp.AccountId;

            }

            return _opp;
        }


        public List<SFLineItem> GetLineItems(string AccountID)
        {
            List<SFLineItem> lstLineItems = new List<SFLineItem>();
            // Create a service object
            binding = new SforceService();
            // Timeout after a minute
            binding.Timeout = System.Convert.ToInt32(ConfigurationManager.AppSettings["SFTimeOutMs"]);
            binding.SessionHeaderValue = new SessionHeader();
            binding.SessionHeaderValue.sessionId = string.Empty;

            //The results will be placed in qr
            QueryResult qr = null;

            //We are going to increase our return batch size to 250 items
            //Setting is a recommendation only, different batch sizes may
            //be returned depending on data, to keep performance optimized.
            binding.QueryOptionsValue = new QueryOptions();
            binding.QueryOptionsValue.batchSize = int.Parse(ConfigurationManager.AppSettings["SF_Accounts_Batch_Size"]); //max seems to be 2000;
            binding.QueryOptionsValue.batchSizeSpecified = true;
            /*
                        Customer type (Direct/Indirect) - 
            Maintenance policy (standard / enhanced..) - Maintenance_program__c
            Product (NIM / RTI ..) - Product_Family__c
            Maintenance %  -  Maintenance__c
            Maintenance source (List /Net) -  Main_Base_for_calculation__c
            Annual increase % (= CPI) - Annual_Increase__c
                        */
            string sQuery = "select Id,Name, Direct_Indirect__c,Maintenance_program__c,Product_Family__c,Maintenance__c,Main_Base_for_calculation__c, Annual_Increase__c from Master_Agreement_Line__c where Master_Agreement__c in (select Master_Agreement__c from Account_Agreement__c where   Account__c ='" + AccountID + "')";


            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                foreach (var item in qr.records)
                {
                    SForce.Master_Agreement_Line__c LineItem = item as SForce.Master_Agreement_Line__c;
                    SFLineItem newLineItem = new SFLineItem();

                    newLineItem.AnnualIncreasePCT = LineItem.Annual_Increase__c;
                    newLineItem.MaintenancePCT = LineItem.Maintenance__c.ToString();
                    newLineItem.MaintenanceSource = LineItem.Main_Base_for_calculation__c;
                    newLineItem.Product = LineItem.Product_Family__c;
                    newLineItem.MaintenancePolicy = LineItem.Maintenance_program__c;
                    newLineItem.CustomerType = LineItem.Direct_Indirect__c;

                    lstLineItems.Add(newLineItem);
                }
            }

            return lstLineItems;
        }


        public string CreateQuote(DBQuote Quote)
        {
            string sRet = string.Empty;

            binding = new SforceService();
            // Timeout after a minute
            binding.Timeout = System.Convert.ToInt32(ConfigurationManager.AppSettings["SFTimeOutMs"]);
            binding.SessionHeaderValue = new SessionHeader();
            binding.SessionHeaderValue.sessionId = string.Empty;

            sObject[] upserts = new Quote[1];
            Quote upsertQuote = new Quote();
            List<SFOpportunity> sfOpps = SearchOpp(Quote.Header.OPPORTUNITY_NUMBER, string.Empty, string.Empty, string.Empty, string.Empty);

            if (sfOpps != null && sfOpps.Count > 0)
            {
                upsertQuote.OpportunityId = sfOpps[0].OppID;
            }

            upsertQuote.Quote_Number__c = Quote.Header.QUOTE_NUMBER;
            upsertQuote.Status = Quote.Header.QUOTE_STATUS;
            upsertQuote.Name = Quote.Header.QUOTE_NUMBER;
            upsertQuote.Quote_Owner__c = Quote.Header.QUOTE_OWNER;
            upsertQuote.Pricebook2Id = "01s2C000000CzA8QAK";
            upserts[0] = upsertQuote;

            try
            {
                // Invoke the upsert call and save the results.
                // Use External_Id custom field for matching records.
                UpsertResult[] upsertResults =
                      binding.upsert("Quote_Number__c", upserts);
                foreach (UpsertResult result in upsertResults)
                {
                    if (result.success)
                    {
                        sRet = SetQuoteLineItems(Quote, result.id, sfOpps[0].OppID);
                    }
                    else
                    {
                       sRet = "The Upsert failed because: " + result.errors[0].message;
                    }
                }
            }
            catch (SoapException e)
            {
                sRet = "An unexpected error has occurred: " +
                                            e.Message + "\n" + e.StackTrace;
            }

            return sRet;
        }

        private string SetQuoteLineItems(DBQuote Quote, string SFQuoteID, string SFOppID)
        {
            string sRet = string.Empty;

            List<SFDivisionLine> sfLines;
            DatabaseAccess dAccess = new DatabaseAccess();

            DeleteQuoteLineItems(Quote.Header.QUOTE_NUMBER);
            sfLines = dAccess._OracleRepo.Get_Quote_Amts_By_Division(Quote.Header.HEADER_ID);

            sObject[] inserts = new QuoteLineItem[sfLines.Count];

            for (int i = 0; i < sfLines.Count; i++)
            {
                QueryResult qr = null;
                string sQuery = "Select id,pricebook2id,product2.family FROM PricebookEntry WHERE Pricebook2iD='01s2C000000CzA8' and product2.family ='" + sfLines[i].DIVISION + "'";

                qr = binding.query(sQuery);

                if (qr.size > 0)
                {
                    SForce.PricebookEntry item = qr.records[0] as SForce.PricebookEntry;
                    QuoteLineItem quoteLineItem = new QuoteLineItem();

                    quoteLineItem.PricebookEntryId = item.Id;
                    quoteLineItem.ACV__c = double.Parse(sfLines[i].ANNUAL_PRICE_INCL_COL);
                    quoteLineItem.Quantity = 1;
                    quoteLineItem.QuantitySpecified = true;
                    quoteLineItem.UnitPrice = double.Parse(sfLines[i].PRICE_INCLUDING_COL);
                    quoteLineItem.UnitPriceSpecified = true;
                    quoteLineItem.QuoteId = SFQuoteID;

                    inserts[i] = quoteLineItem;
                }
            }

            try
            {
                // Invoke the insert call and save the results.
                SaveResult[] insertResults = binding.create(inserts);

                if (insertResults != null)
                {
                    foreach (SaveResult result in insertResults)
                    {
                        if (!result.success)
                        {
                            sRet += "The insert failed because: " +
                                  result.errors[0].message + "******" +
                                  "Quote ID = " + SFQuoteID + "******" +
                                  "PricebookEntryID = " + ((QuoteLineItem)inserts[0]).PricebookEntryId + "******" +
                                  "Opp ID = " + SFOppID;
                        }
                    }
                }
            }
            catch (SoapException e)
            {
                sRet = "An unexpected error has occurred: " +
                                           e.Message + "\n" + e.StackTrace;
            }
            return sRet;
        }
        private void DeleteQuoteLineItems(string QuoteID)
        {
            QueryResult qr = null;
            string sQuery = "select id, quoteid, linenumber,listprice, PricebookEntryId, Quote.Quote_Number__c from quotelineitem";


            qr = binding.query(sQuery);

            if (qr.size > 0)
            {
                foreach (SForce.QuoteLineItem item in qr.records)
                {

                    if (item.Quote.Quote_Number__c == QuoteID)
                    {
                        string s = item.QuoteId;

                        s = item.PricebookEntryId;
                        s = item.Id;
                        string[] IDs = new string[1];

                        IDs[0] = item.Id;
                        DeleteResult[] deleteResults = binding.delete(IDs);

                        for (int i = 0; i < deleteResults.Length; i++)
                        {

                            DeleteResult deleteResult = deleteResults[i];

                            if (deleteResult.success)
                            {

                                Console.WriteLine("Deleted Record ID: " + deleteResult.id);

                            }

                            else
                            {

                                // Handle the errors.

                                // We just print the first error out for sample purposes.

                                Error[] errors = deleteResult.errors;

                                if (errors.Length > 0)
                                {

                                    Console.WriteLine("Error: could not delete " + "Record ID "

                                          + deleteResult.id + ".");

                                    Console.WriteLine("   The error reported was: ("

                                          + errors[0].statusCode + ") "

                                          + errors[0].message + "\n");

                                }

                            }

                        }

                    }
                }
            }

        }
        public List<SFAccount> searchAccountByName(string searchPhrase)
        {
            List<SFAccount> lstAccounts = new List<SFAccount>();
            bool bIsSFLogin = SFLogin();

            if (bIsSFLogin)
            {
                lstAccounts = QueryAccounts(searchPhrase);
            }

            return lstAccounts;
        }
    }
}